---
title: "Convex & Clerk"
sidebar_label: "Clerk"
sidebar_position: 10
description: "Integrate Clerk authentication with Convex"
---

import UnderTheHood from "@site/docs/auth/_under_the_hood.mdx";
import ConfigTS from "!!raw-loader!@site/../private-demos/quickstarts/react-vite-ts/src/_mainClerk.tsx";
import ConfigJS from "!!raw-loader!@site/../private-demos/quickstarts/react-vite/src/_mainClerk.jsx";
import ConfigEnvTS from "!!raw-loader!@site/../private-demos/quickstarts/react-vite-ts/src/_mainClerkEnv.tsx";
import ConfigEnvJS from "!!raw-loader!@site/../private-demos/quickstarts/react-vite/src/_mainClerkEnv.jsx";
import App from "!!raw-loader!@site/../private-demos/snippets/src/clerkApp.tsx";
import Messages from "!!raw-loader!@site/../private-demos/snippets/convex/clerkMessages.ts";

[Clerk](https://clerk.com) is an authentication platform providing login via
passwords, social identity providers, one-time email or SMS access codes, and
multi-factor authentication and user management.

## Get started

Convex offers a provider that is specifically for integrating with Clerk called
`<ConvexProviderWithClerk>`. It works with any of Clerk's React-based SDKs, such
as the Next.js and Expo SDKs.

See the following sections for the Clerk SDK that you're using:

- [React](#react) - Use this as a starting point if your SDK is not listed
- [Next.js](#nextjs)
- [TanStack Start](#tanstack-start)

### React

**Example:**
[React with Convex and Clerk](https://github.com/get-convex/template-react-vite-clerk)

This guide assumes you already have a working React app with Convex. If not
follow the [Convex React Quickstart](/quickstart/react.mdx) first. Then:

<StepByStep>
  <Step title="Sign up for Clerk">
    Sign up for a free Clerk account at [clerk.com/sign-up](https://dashboard.clerk.com/sign-up).

    <p style={{textAlign: 'center'}}>
      <img src="/screenshots/clerk-signup.png" alt="Sign up to Clerk" width={200} />
    </p>

  </Step>
  <Step title="Create an application in Clerk">
    Choose how you want your users to sign in.

    <p style={{textAlign: 'center'}}>
      <img src="/screenshots/clerk-createapp.png" alt="Create a Clerk application" width={200} />
    </p>

  </Step>
  <Step title="Create a JWT Template">
    In the Clerk Dashboard, navigate to the [JWT templates](https://dashboard.clerk.com/last-active?path=jwt-templates) page.

    Select _New template_ and then from the list of templates, select _Convex_. You'll be redirected to the template's settings page. **Do NOT rename the JWT token. It must be called `convex`.**

    Copy and save the _Issuer_ URL somewhere secure. This URL is the issuer domain for Clerk's JWT templates, which is your Clerk app's _Frontend API URL_. In development, it's format will be `https://verb-noun-00.clerk.accounts.dev`. In production, it's format will be `https://clerk.<your-domain>.com`.

    <p style={{textAlign: 'center'}}>
      <img src="/screenshots/clerk-createjwt.png" alt="Create a JWT template" width={400} />
    </p>

  </Step>
  <Step title="Configure Convex with the Clerk issuer domain">
    In your app's `convex` folder, create a new file <JSDialectFileName name="auth.config.ts" /> with the following code. This is the server-side configuration for validating access tokens.

    ```ts title="convex/auth.config.ts"
    import { AuthConfig } from "convex/server";

    export default {
      providers: [
        {
          // Replace with your own Clerk Issuer URL from your "convex" JWT template
          // or with `process.env.CLERK_JWT_ISSUER_DOMAIN`
          // and configure CLERK_JWT_ISSUER_DOMAIN on the Convex Dashboard
          // See https://docs.convex.dev/auth/clerk#configuring-dev-and-prod-instances
          domain: process.env.CLERK_JWT_ISSUER_DOMAIN!,
          applicationID: "convex",
        },
      ]
    } satisfies AuthConfig;
    ```

  </Step>
  <Step title="Deploy your changes">
    Run `npx convex dev` to automatically sync your configuration to your backend.

    ```sh
    npx convex dev
    ```

  </Step>
  <Step title="Install clerk">
    In a new terminal window, install the Clerk React SDK:

    ```sh
    npm install @clerk/clerk-react
    ```

  </Step>
  <Step title="Set your Clerk API keys">
    In the Clerk Dashboard, navigate to the [**API keys**](https://dashboard.clerk.com/last-active?path=api-keys) page. In the **Quick Copy** section, copy your Clerk Publishable Key and set it as the `CLERK_PUBLISHABLE_KEY` environment variable. If you're using Vite, you will need to prefix it with `VITE_`.

    ```env title=".env"
    VITE_CLERK_PUBLISHABLE_KEY=YOUR_PUBLISHABLE_KEY
    ```

  </Step>
  <Step title="Configure ConvexProviderWithClerk">
    Both Clerk and Convex have provider components that are required to provide authentication and client context.

    You should already have `<ConvexProvider>` wrapping your app. Replace it with `<ConvexProviderWithClerk>`, and pass Clerk's `useAuth()` hook to it.

    Then, wrap it with `<ClerkProvider>`. `<ClerkProvider>` requires a `publishableKey` prop, which you can set to the `VITE_CLERK_PUBLISHABLE_KEY` environment variable.

    <TSAndJSSnippet
      title="src/main.tsx"
      sourceTS={ConfigTS}
      sourceJS={ConfigJS}
      highlightPatterns={["ClerkProvider", "ConvexProviderWithClerk"]}
    />

  </Step>

  <Step title="Show UI based on authentication state">
    You can control which UI is shown when the user is signed in or signed out using Convex's `<Authenticated>`, `<Unauthenticated>` and `<AuthLoading>` helper components. These should be used instead of Clerk's `<SignedIn>`, `<SignedOut>` and `<ClerkLoading>` components, respectively.

    It's important to use the [`useConvexAuth()`](/api/modules/react#useconvexauth) hook instead of
    Clerk's `useAuth()` hook when you need to check whether the user is logged in or
    not. The `useConvexAuth()` hook makes sure that the browser has fetched the auth
    token needed to make authenticated requests to your Convex backend, and that the
    Convex backend has validated it.

    In the following example, the `<Content />` component is a child of `<Authenticated>`, so its content and any of its child components are guaranteed to have an authenticated user, and Convex queries can require authentication.

    ```tsx title="src/App.tsx"
    import { SignInButton, UserButton } from "@clerk/clerk-react";
    import { Authenticated, Unauthenticated, AuthLoading, useQuery } from "convex/react";
    import { api } from "../convex/_generated/api";

    function App() {
      return (
        <main>
          <Unauthenticated>
            <SignInButton />
          </Unauthenticated>
          <Authenticated>
            <UserButton />
            <Content />
          </Authenticated>
          <AuthLoading>
            <p>Still loading</p>
          </AuthLoading>
        </main>
      );
    }

    function Content() {
      const messages = useQuery(api.messages.getForCurrentUser);
      return <div>Authenticated content: {messages?.length}</div>;
    }

    export default App;
    ```

  </Step>

  <Step title="Use authentication state in your Convex functions">
    If the client is authenticated, you can access the information
    stored in the JWT via `ctx.auth.getUserIdentity`.

    If the client isn't authenticated, `ctx.auth.getUserIdentity` will return `null`.

    **Make sure that the component calling this query is a child of `<Authenticated>` from
    `convex/react`**. Otherwise, it will throw on page load.

    <TSAndJSSnippet
      title="convex/messages.ts"
      sourceTS={Messages}
      sourceJS={Messages}
    />

  </Step>
</StepByStep>

### Next.js

**Example:**
[Next.js with Convex and Clerk](https://github.com/get-convex/template-nextjs-clerk)

This guide assumes you already have a working Next.js app with Convex. If not
follow the [Convex Next.js Quickstart](/quickstart/nextjs.mdx) first. Then:

<StepByStep>
  <Step title="Sign up for Clerk">
    Sign up for a free Clerk account at [clerk.com/sign-up](https://dashboard.clerk.com/sign-up).

    <p style={{textAlign: 'center'}}>
      <img src="/screenshots/clerk-signup.png" alt="Sign up to Clerk" width={200} />
    </p>

  </Step>
  <Step title="Create an application in Clerk">
    Choose how you want your users to sign in.

    <p style={{textAlign: 'center'}}>
      <img src="/screenshots/clerk-createapp.png" alt="Create a Clerk application" width={200} />
    </p>

  </Step>
  <Step title="Create a JWT Template">
    In the Clerk Dashboard, navigate to the [JWT templates](https://dashboard.clerk.com/last-active?path=jwt-templates) page.

    Select _New template_ and then from the list of templates, select _Convex_. You'll be redirected to the template's settings page. **Do NOT rename the JWT token. It must be called `convex`.**

    Copy and save the _Issuer_ URL somewhere secure. This URL is the issuer domain for Clerk's JWT templates, which is your Clerk app's _Frontend API URL_. In development, it's format will be `https://verb-noun-00.clerk.accounts.dev`. In production, it's format will be `https://clerk.<your-domain>.com`.

    <p style={{textAlign: 'center'}}>
      <img src="/screenshots/clerk-createjwt.png" alt="Create a JWT template" width={400} />
    </p>

  </Step>
  <Step title="Configure Convex with the Clerk issuer domain">
    In your app's `convex` folder, create a new file <JSDialectFileName name="auth.config.ts" /> with the following code. This is the server-side configuration for validating access tokens.

    ```ts title="convex/auth.config.ts"
    import { AuthConfig } from "convex/server";

    export default {
      providers: [
        {
          // Replace with your own Clerk Issuer URL from your "convex" JWT template
          // or with `process.env.CLERK_JWT_ISSUER_DOMAIN`
          // and configure CLERK_JWT_ISSUER_DOMAIN on the Convex Dashboard
          // See https://docs.convex.dev/auth/clerk#configuring-dev-and-prod-instances
          domain: process.env.CLERK_JWT_ISSUER_DOMAIN!,
          applicationID: "convex",
        },
      ]
    } satisfies AuthConfig;
    ```

  </Step>
  <Step title="Deploy your changes">
    Run `npx convex dev` to automatically sync your configuration to your backend.

    ```sh
    npx convex dev
    ```

  </Step>
  <Step title="Install clerk">
    In a new terminal window, install the Clerk Next.js SDK:

    ```sh
    npm install @clerk/nextjs
    ```

  </Step>
  <Step title="Set your Clerk API keys">
    In the Clerk Dashboard, navigate to the [**API keys**](https://dashboard.clerk.com/last-active?path=api-keys) page. In the **Quick Copy** section, copy your Clerk Publishable and Secret Keys and set them as the `NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY` and `CLERK_SECRET_KEY` environment variables, respectively.

    ```env title=".env"
    NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY=YOUR_PUBLISHABLE_KEY
    CLERK_SECRET_KEY=YOUR_SECRET_KEY
    ```

  </Step>
  <Step title="Add Clerk middleware">
    Clerk's `clerkMiddleware()` helper grants you access to user authentication state throughout your app.

    Create a `middleware.ts` file.

    In your `middleware.ts` file, export the `clerkMiddleware()` helper:

    ```tsx {{ filename: 'middleware.ts' }}
    import { clerkMiddleware } from '@clerk/nextjs/server'

    export default clerkMiddleware()

    export const config = {
      matcher: [
        // Skip Next.js internals and all static files, unless found in search params
        '/((?!_next|[^?]*\\.(?:html?|css|js(?!on)|jpe?g|webp|png|gif|svg|ttf|woff2?|ico|csv|docx?|xlsx?|zip|webmanifest)).*)',
        // Always run for API routes
        '/(api|trpc)(.*)',
      ],
    }
    ```

    By default, `clerkMiddleware()` will not protect any routes. All routes are public and you must opt-in to protection for routes.https://clerk.com/docs/references/nextjs/clerk-middleware) to learn how to require authentication for specific routes.

  </Step>
  <Step title="Configure ConvexProviderWithClerk">
    Both Clerk and Convex have provider components that are required to provide authentication and client context.

    Typically, you'd replace `<ConvexProvider>` with `<ConvexProviderWithClerk>`, but with Next.js App Router, things are a bit more complex.

    `<ConvexProviderWithClerk>` calls `ConvexReactClient()` to get Convex's client, so it must be used in a Client Component. Your `app/layout.tsx`, where you would use `<ConvexProviderWithClerk>`, is a Server Component, and a Server Component cannot contain Client Component code. To solve this, you must first create a _wrapper_ Client Component around `<ConvexProviderWithClerk>`.

    ```tsx {{ filename: 'components/ConvexClientProvider.tsx' }}
    'use client'

    import { ReactNode } from 'react'
    import { ConvexReactClient } from 'convex/react'
    import { ConvexProviderWithClerk } from 'convex/react-clerk'
    import { useAuth } from '@clerk/nextjs'

    if (!process.env.NEXT_PUBLIC_CONVEX_URL) {
      throw new Error('Missing NEXT_PUBLIC_CONVEX_URL in your .env file')
    }

    const convex = new ConvexReactClient(process.env.NEXT_PUBLIC_CONVEX_URL)

    export default function ConvexClientProvider({ children }: { children: ReactNode }) {
      return (
        <ConvexProviderWithClerk client={convex} useAuth={useAuth}>
          {children}
        </ConvexProviderWithClerk>
      )
    }
    ```

  </Step>
  <Step title="Wrap your app in Clerk and Convex">
    Now, your Server Component, `app/layout.tsx`, can render `<ConvexClientProvider>` instead of rendering `<ConvexProviderWithClerk>` directly. It's important that `<ClerkProvider>` wraps `<ConvexClientProvider>`, and not the other way around, as Convex needs to be able to access the Clerk context.

    ```tsx {{ filename: 'app/layout.tsx', mark: [5, 31] }}
    import type { Metadata } from 'next'
    import { Geist, Geist_Mono } from 'next/font/google'
    import './globals.css'
    import { ClerkProvider } from '@clerk/nextjs'
    import ConvexClientProvider from '@/components/ConvexClientProvider'

    const geistSans = Geist({
      variable: '--font-geist-sans',
      subsets: ['latin'],
    })

    const geistMono = Geist_Mono({
      variable: '--font-geist-mono',
      subsets: ['latin'],
    })

    export const metadata: Metadata = {
      title: 'Clerk Next.js Quickstart',
      description: 'Generated by create next app',
    }

    export default function RootLayout({
      children,
    }: Readonly<{
      children: React.ReactNode
    }>) {
      return (
        <html lang="en">
          <body className={`${geistSans.variable} ${geistMono.variable} antialiased`}>
            <ClerkProvider>
              <ConvexClientProvider>{children}</ConvexClientProvider>
            </ClerkProvider>
          </body>
        </html>
      )
    }
    ```

  </Step>
  <Step title="Show UI based on authentication state">
    You can control which UI is shown when the user is signed in or signed out using Convex's `<Authenticated>`, `<Unauthenticated>` and `<AuthLoading>` helper components. These should be used instead of Clerk's `<SignedIn>`, `<SignedOut>` and `<ClerkLoading>` components, respectively.

    It's important to use the [`useConvexAuth()`](/api/modules/react#useconvexauth) hook instead of
    Clerk's `useAuth()` hook when you need to check whether the user is logged in or
    not. The `useConvexAuth()` hook makes sure that the browser has fetched the auth
    token needed to make authenticated requests to your Convex backend, and that the
    Convex backend has validated it.

    In the following example, the `<Content />` component is a child of `<Authenticated>`, so its content and any of its child components are guaranteed to have an authenticated user, and Convex queries can require authentication.

    ```tsx title="app/page.tsx"
    "use client";

    import { Authenticated, Unauthenticated } from "convex/react";
    import { SignInButton, UserButton } from "@clerk/nextjs";
    import { useQuery } from "convex/react";
    import { api } from "../convex/_generated/api";

    export default function Home() {
      return (
        <>
          <Authenticated>
            <UserButton />
            <Content />
          </Authenticated>
          <Unauthenticated>
            <SignInButton />
          </Unauthenticated>
        </>
      );
    }

    function Content() {
      const messages = useQuery(api.messages.getForCurrentUser);
      return <div>Authenticated content: {messages?.length}</div>;
    }
    ```

  </Step>

  <Step title="Use authentication state in your Convex functions">
    If the client is authenticated, you can access the information
    stored in the JWT via `ctx.auth.getUserIdentity`.

    If the client isn't authenticated, `ctx.auth.getUserIdentity` will return `null`.

    **Make sure that the component calling this query is a child of `<Authenticated>` from
    `convex/react`**. Otherwise, it will throw on page load.

    <TSAndJSSnippet
      title="convex/messages.ts"
      sourceTS={Messages}
      sourceJS={Messages}
    />

  </Step>
</StepByStep>

### TanStack Start

**Example:**
[TanStack Start with Convex and Clerk](https://github.com/get-convex/templates/tree/main/template-tanstack-start)

See the
[TanStack Start with Clerk guide](/client/tanstack/tanstack-start/clerk.mdx) for
more information.

## Next steps

### Accessing user information in functions

See [Auth in Functions](/auth/functions-auth.mdx) to learn about how to access
information about the authenticated user in your queries, mutations and actions.

See [Storing Users in the Convex Database](/auth/database-auth.mdx) to learn
about how to store user information in the Convex database.

### Accessing user information client-side

To access the authenticated user's information, use Clerk's `User` object, which
can be accessed using Clerk's
[`useUser()`](https://clerk.com/docs/hooks/use-user) hook. For more information
on the `User` object, see the
[Clerk docs](https://clerk.com/docs/references/javascript/user).

```tsx title="components/Badge.tsx"
export default function Badge() {
  const { user } = useUser();

  return <span>Logged in as {user.fullName}</span>;
}
```

## Configuring dev and prod instances

To configure a different Clerk instance between your Convex development and
production deployments, you can use environment variables configured on the
Convex dashboard.

### Configuring the backend

In the Clerk Dashboard, navigate to the
[**API keys**](https://dashboard.clerk.com/last-active?path=api-keys) page. Copy
your Clerk Frontend API URL. This URL is the issuer domain for Clerk's JWT
templates, and is necessary for Convex to validate access tokens. In
development, it's format will be `https://verb-noun-00.clerk.accounts.dev`. In
production, it's format will be `https://clerk.<your-domain>.com`.

Paste your Clerk Frontend API URL into your `.env` file, set it as the
`CLERK_JWT_ISSUER_DOMAIN` environment variable.

```env title=".env"
CLERK_JWT_ISSUER_DOMAIN=https://verb-noun-00.clerk.accounts.dev
```

Then, update your <JSDialectFileName name="auth.config.ts" /> file to use the
environment variable.

```ts title="convex/auth.config.ts"
import { AuthConfig } from "convex/server";

export default {
  providers: [
    {
      domain: process.env.CLERK_JWT_ISSUER_DOMAIN!,
      applicationID: "convex",
    },
  ],
} satisfies AuthConfig;
```

**Development configuration**

In the left sidenav of the Convex [dashboard](https://dashboard.convex.dev),
switch to your development deployment and set the values for your development
Clerk instance.

{/* TODO: Update screenshot to use `CLERK_FRONTEND_API_URL`. It should be in the format `https://verb-noun-00.clerk.accounts.dev` */}

<p style={{ textAlign: "center" }}>
  <img
    src="/screenshots/clerk-convex-dashboard.png"
    alt="Convex dashboard dev deployment settings"
    width={600}
  />
</p>

Then, to switch your deployment to the new configuration, run `npx convex dev`.

**Production configuration**

In the left sidenav of the Convex [dashboard](https://dashboard.convex.dev),
switch to your production deployment and set the values for your production
Clerk instance.

{/* TODO: Add screenshot of production configuration in Convex dashboard. The `CLERK_FRONTEND_API_URL` should be in the format `https://clerk.<your-domain>.com` */}

Then, to switch your deployment to the new configuration, run
`npx convex deploy`.

### Configuring Clerk's API keys

Clerk's API keys differ depending on whether they are for development or
production. Don't forget to update the environment variables in your `.env` file
as well as your hosting platform, such as Vercel or Netlify.

**Development configuration**

Clerk's Publishable Key for development follows the format `pk_test_...`.

```py title=".env.local"
VITE_CLERK_PUBLISHABLE_KEY="pk_test_..."
```

**Production configuration**

Clerk's Publishable Key for production follows the format `pk_live_...`.

```py title=".env"
NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY="pk_live_..."
```

## Debugging authentication

If a user goes through the Clerk login flow successfully, and after being
redirected back to your page, `useConvexAuth()` returns
`isAuthenticated: false`, it's possible that your backend isn't correctly
configured.

The <JSDialectFileName name="auth.config.ts" /> file contains a list of
configured authentication providers. You must run `npx convex dev` or
`npx convex deploy` after adding a new provider to sync the configuration to
your backend.

For more thorough debugging steps, see
[Debugging Authentication](/auth/debug.mdx).

## Under the hood

<UnderTheHood
  provider="Clerk"
  integrationProvider={<code>ConvexProviderWithClerk</code>}
  providerProvider={<code>ClerkProvider</code>}
  configProp={
    <>
      the{" "}
      <a
        href="https://clerk.com/docs/authentication/sign-in#override-ur-ls"
        target="_blank"
      >
        <code>afterSignIn</code>
      </a>{" "}
      prop
    </>
  }
/>
